package org.xteam.sled.parser;

import java_cup.runtime.*;
import java.io.*;
import org.xteam.parser.runtime.IErrorReporter;

%%

%cupsym ISledTokens
%cup
%class SledLexer
%public
%char

DIGIT		= [0-9]
LETTER		= [a-zA-Z_\.]
HEXA		= [a-fA-F0-9]
IDENT		= {LETTER}({LETTER}|{DIGIT})*
INT			= {DIGIT}+|0[xX]{HEXA}+
COMMENT		= "#" .*

%{

	private StringBuffer buffer = new StringBuffer();
	private int lastState = YYINITIAL;
	private int lastPosition;
	private IErrorReporter reporter;
	
	public SledLexer(Reader reader, IErrorReporter reporter) {
		this(reader);
		this.reporter = reporter;
	}

	public Symbol symbol(int kind) {
		return new Symbol(kind, yychar, yychar+yylength());
	}
	
	public Symbol symbol(int kind, Object value) {
		return new Symbol(kind, yychar, yychar+yylength(), value);
	}
	
	public Symbol symbol(int kind, Object value, int position, int length) {
		return new Symbol(kind, position, position+length, value);
	}
	
	public void startNewLine() {
		//System.out.println("$newline$");
		yybegin(NL);
	}
	
	public void startOperands() {
		//System.out.println("$try-operand " + yystate() + "$");
		if (yystate() == NL) {
			//System.out.println("$operand$");
			yybegin(OPERAND);
		}
	}
	
	private void badCharacter(String msg) {
		reporter.reportError(yychar, yylength(), msg);
	}
	
%}

%state STR OPERAND NL

%%

<YYINITIAL> {
{COMMENT}		{ /* ignore comments */ }
"\n"			{ /* ignore newlines */ }
[ \t\f\b]+		{ /* ignore whites */  }
"any"			{ return symbol(ISledTokens.ANY); }
"bit"			{ return symbol(ISledTokens.BIT); }
"checked"		{ return symbol(ISledTokens.CHECKED); }
"constructors"	{ return symbol(ISledTokens.CONSTRUCTORS); }
"epsilon"		{ return symbol(ISledTokens.EPSILON); }
"fieldinfo"		{ return symbol(ISledTokens.FIELDINFO); }
"fields"		{ return symbol(ISledTokens.FIELDS); }
"for"			{ return symbol(ISledTokens.FOR); }
"guaranteed"	{ return symbol(ISledTokens.GUARANTEED); }
"is"			{ return symbol(ISledTokens.IS); }
"names"			{ return symbol(ISledTokens.NAMES); }
"of"			{ return symbol(ISledTokens.OF); }
"otherwise"		{ return symbol(ISledTokens.OTHERWISE); }
"patterns"		{ return symbol(ISledTokens.PATTERNS); }
"placeholder"	{ return symbol(ISledTokens.PLACEHOLDER); }
"relocatable"	{ return symbol(ISledTokens.RELOCATABLE); }
"significant"	{ return symbol(ISledTokens.SIGNIFICANT); }
"sparse"		{ return symbol(ISledTokens.SPARSE); }
"to"			{ return symbol(ISledTokens.TO); }
"unchecked"		{ return symbol(ISledTokens.UNCHECKED); }
"when"			{ return symbol(ISledTokens.WHEN); }
"which"			{ return symbol(ISledTokens.WHICH); }

"("			{ return symbol(ISledTokens.LPAR); }
")"			{ return symbol(ISledTokens.RPAR); }
"{"			{ return symbol(ISledTokens.LBRC); }
"}"			{ return symbol(ISledTokens.RBRC); }
"["			{ return symbol(ISledTokens.LBRT); }
"]"			{ return symbol(ISledTokens.RBRT); }
":"			{ return symbol(ISledTokens.COLO); }
","			{ return symbol(ISledTokens.COMA); }
"!"			{ return symbol(ISledTokens.EXCL); }
"&"			{ return symbol(ISledTokens.AND); }
"|"			{ return symbol(ISledTokens.OR); }
";"			{ return symbol(ISledTokens.SEMI); }
"+"			{ return symbol(ISledTokens.PLUS); }
"-"			{ return symbol(ISledTokens.MINUS); }
"*"			{ return symbol(ISledTokens.TIMES); }
"/"			{ return symbol(ISledTokens.DIV); }
"@"			{ return symbol(ISledTokens.AT); }
"="			{ return symbol(ISledTokens.EQUALS); }
"^"			{ return symbol(ISledTokens.HAT); }
"<"			{ return symbol(ISledTokens.LT); }
"<="		{ return symbol(ISledTokens.LE); }
">"			{ return symbol(ISledTokens.GT); }
">="		{ return symbol(ISledTokens.GE); }
"!="		{ return symbol(ISledTokens.NE); }
"..."		{ return symbol(ISledTokens.DOTS); }
\"			{ buffer.setLength(0); lastPosition = yychar; lastState = yystate(); yybegin(STR); }

{INT}		{ return symbol(ISledTokens.INT, Integer.decode(yytext())); }
{IDENT}		{ return symbol(ISledTokens.IDENT, yytext()); }
.			{ badCharacter("bad character \'"+yycharat(0)+"\'"); }
}

<OPERAND,NL> {
{COMMENT}			{ /* ignore comments */ }
"\n"			{ yybegin(YYINITIAL); return symbol(ISledTokens.NEWLINE); }
<<EOF>>			{ yybegin(YYINITIAL); return symbol(ISledTokens.NEWLINE); }
":"			{ yybegin(YYINITIAL); return symbol(ISledTokens.COLO); }
"{"			{ yybegin(YYINITIAL); return symbol(ISledTokens.LBRC); }
\"			{ buffer.setLength(0); lastPosition = yychar; lastState = yystate(); yybegin(STR); }
"is"		{ yybegin(YYINITIAL); return symbol(ISledTokens.IS); }
"otherwise"	{ yybegin(YYINITIAL); return symbol(ISledTokens.OTHERWISE); }
"when"		{ yybegin(YYINITIAL); return symbol(ISledTokens.WHEN); }
{IDENT}		{ return symbol(ISledTokens.IDENT, yytext()); }
}

<NL> {
[ \t\f\b]+	{ /* ignore whites */ }
[,()\[\]]+	{ return symbol(ISledTokens.NOISE, yytext()); }
"^"			{ return symbol(ISledTokens.HAT); }
.			{ badCharacter("bad character \'"+yycharat(0)+"\' in opcode"); }
}

<OPERAND> {
[,()\[\] \t\f\b]+	{ return symbol(ISledTokens.NOISE, yytext()); }
"!"			{ return symbol(ISledTokens.EXCL); }
.			{ badCharacter("bad character \'"+yycharat(0)+"\' in operand"); }
}

<STR> {
\"					{ yybegin(lastState);
							return symbol(
								ISledTokens.STRING,
								buffer.toString(), lastPosition, buffer.length()+2); }
[^\"\\]+			{ buffer.append(yytext()); }
\\\"				{ buffer.append('\"'); }
\\					{ buffer.append('\\'); }
}
